/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.iec.edp.caf.tenancy.core.service;

import io.iec.edp.caf.boot.context.CAFContext;
import io.iec.edp.caf.commons.dataaccess.DbType;
import io.iec.edp.caf.commons.runtime.CafEnvironment;
import io.iec.edp.caf.commons.utils.SpringBeanUtils;
import io.iec.edp.caf.commons.utils.StringUtils;
import io.iec.edp.caf.core.context.ICAFContextService;
import io.iec.edp.caf.data.source.UriInfo;
import io.iec.edp.caf.tenancy.api.ITenantService;
import io.iec.edp.caf.tenancy.api.entity.AppInstanceInfo;
import io.iec.edp.caf.tenancy.api.entity.DbConnectionInfo;
import io.iec.edp.caf.tenancy.api.entity.TenancyMode;
import io.iec.edp.caf.tenancy.api.entity.Tenant;
import io.iec.edp.caf.tenancy.core.properties.MultiTenantProperties;
import io.iec.edp.caf.tenancy.core.utils.DataValidator;
import lombok.var;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;

import java.io.IOException;
import java.util.Date;
import java.util.List;

//todo 被io.iec.edp.caf.runtime.identity.config.UserMapperConfig,io.iec.edp.caf.authentication.userpassword.core.config.CAFAuthUserPasswordAutoConfiguration依赖
public class TenantService implements ITenantService {
    private TenantManager tenantmgr;

    private DbMappingManager dbMappingMgr;

    private DbConnectionManager dbMgr;

    private TenantAppInstanceMgr appInstMgr;

    private ClusterManager clusterMgr;
    private ICAFContextService context;

    private OkHttpClient httpClient;
    private TenancyMode mode = TenancyMode.exclusive;

    public TenantService(MultiTenantProperties properties, TenantManager tenantmgr, DbMappingManager dbMappingMgr, DbConnectionManager dbMgr,
                         TenantAppInstanceMgr appInstMgr, ClusterManager clusterMgr) {
        this.tenantmgr = tenantmgr;
        this.dbMappingMgr = dbMappingMgr;
        this.dbMgr = dbMgr;
        this.appInstMgr = appInstMgr;
        this.clusterMgr = clusterMgr;
        this.context = SpringBeanUtils.getBean(ICAFContextService.class);

        //okhttp的client最好是创建一个吗，频繁的多次创建容易出现OOM
        this.httpClient = new OkHttpClient()
                .newBuilder()
                .followRedirects(false)
                .followSslRedirects(false)
                .build();
        if(properties!=null && !StringUtils.isEmpty(properties.getMode())){
            mode = TenancyMode.valueOf(properties.getMode().toLowerCase());
        }
    }

    /**
     * 获取多租户模式
     * @return
     */
    @Override
    public TenancyMode getTenancyMode(){
        return this.mode;
    }

    @Override
    public List<Tenant> getAllTenants(String language) {
        if (language.isEmpty()) {
            language = this.context.getLanguage();
        }
        if (language.isEmpty()) {
            language = "zh-CHS";
        }

        return this.tenantmgr.getAllTenants(language);
    }

    @Override
    public Tenant getTenant(int tenantId, String language) {
        DataValidator.checkForNullReference(tenantId, "tenantId");

        if (language.isEmpty()) {
            language = this.context.getLanguage();
        }
        if (language.isEmpty()) {
            language = "zh-CHS";
        }

        return this.tenantmgr.get(tenantId, language);
    }

    /**
     * 根据租户编号获取租户基本信息
     * @param tenantCode
     * @param language
     * @return
     */
    @Override
    public Tenant getByCode(String tenantCode, String language) {
        DataValidator.checkForNullReference(tenantCode, "tenantCode");

        if (language.isEmpty())
        {
            language = this.context.getLanguage();
        }
        if (language.isEmpty())
        {
            language = "zh-CHS";
        }

        DataValidator.checkForEmptyString(language, "language");

        return this.tenantmgr.getByCode(tenantCode, language);
    }


    @Override
    public String getApplicationURL(int tenantId, String appInstanceCode) {
        DataValidator.checkForNullReference(tenantId, "tenantId");
        DataValidator.checkForEmptyString(appInstanceCode, "appInstanceCode");

        var appInstanceInfo = this.getAppInstanceInfo(tenantId, appInstanceCode);
        var clusterDef = this.clusterMgr.get(appInstanceInfo.getId());
        String baseUrl = clusterDef.getBaseUrl();
        return baseUrl + "/" + appInstanceInfo.getPortalUrl();
    }

    @Override
    public List<AppInstanceInfo> getAllAppInstInfos(int tenantId) {
        DataValidator.checkForNullReference(tenantId, "tenantId");
        return this.appInstMgr.getAllAppInstInfos(tenantId);
    }

    @Override
    public AppInstanceInfo getAppInstanceInfo(int tenantId, String appInstanceCode) {
        DataValidator.checkForNullReference(tenantId, "tenantId");
        DataValidator.checkForEmptyString(appInstanceCode, "appInstanceCode");

        return this.appInstMgr.get(tenantId, appInstanceCode);
    }

    @Override
    public List<DbConnectionInfo> getAllDBConnInfos(int tenantId, String appInstanceCode) {
        DataValidator.checkForNullReference(tenantId, "tenantId");
        DataValidator.checkForEmptyString(appInstanceCode, "appInstanceCode");

        var appInstanceInfo = this.appInstMgr.get(tenantId, appInstanceCode);
        if (appInstanceInfo == null) {
            return null;
        }
        return this.dbMgr.getByAppInstanceId(appInstanceInfo.getId());
    }

    @Override
    public DbConnectionInfo getDBConnInfo(int tenantId, String appInstanceCode, String su) {

        DataValidator.checkForNullReference(tenantId, "tenantId");
        DataValidator.checkForEmptyString(appInstanceCode, "appInstanceCode");
        DataValidator.checkForEmptyString(su, "su");

        String dbConnectionId = this.getDBConnectionId(tenantId, appInstanceCode, su);
        if (dbConnectionId != null && !dbConnectionId.isEmpty()) {
//            throw new NoMatchingAppInstanceFound("","GetDBConnInfo",
//                    String.format("未指定当前实例信息,租户%d,appInstanceCode:%s,Su:%s", tenantId, appInstanceCode,su),
//                    null, ExceptionLevel.Error, false);
            var connInfo = this.dbMgr.get(dbConnectionId);
            return connInfo;
        }
        return null;
    }

    @Override
    public UriInfo createDbInstance(String schemaName) {
        throw new RuntimeException("Unsupported Function. Try to realize by yourself.");

        //return null;
    }

    @Override
    public void updateTenant(Tenant tenant,int oldTenantId){
        throw new RuntimeException("Unsupported Function. Try to realize by yourself.");
    }

    @Override
    public void saveTenantInfo(Tenant tenant,String dbStr,String appInsId,
                               String appInsName,String appInsDes,List<String> suList) {
        throw new RuntimeException("Unsupported Function. Try to realize by yourself.");
    }

    /// <summary>
    /// 根据租户、实例编号获取数据库编号
    /// </summary>
    /// <param name="tenantId">租户编号</param>
    /// <param name="appInstanceCode">应用实例编号</param>
    /// <param name="su">服务单元</param>
    public String getDBConnectionId(int tenantId, String appInstanceCode, String su) {
        DataValidator.checkForNullReference(tenantId, "tenantId");
        DataValidator.checkForEmptyString(appInstanceCode, "appInstanceCode");
        DataValidator.checkForEmptyString(su, "su");

        //查找实例信息
        var appInstanceInfo = this.appInstMgr.get(tenantId, appInstanceCode);
        //查找su mapping信息
        var mapping = this.dbMappingMgr.get(appInstanceInfo.getId(), su);
        if (mapping == null) {
            return null;
        }

        return mapping.getDbConnectionId();
    }

    /**
     * 根据租户标识获取缓存key
     *
     * @param
     * @return
     * @Date: 2019/8/22
     */
    private String GetCacheKey(int tenantId, String appInstanceCode, String su) {
        return String.format("%d_%s_DbConnId", tenantId, su);
    }


    public void changeTenantId(String openId, String tenantId) throws IOException {
        MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
        RequestBody body = RequestBody.create(mediaType, String.format("openId=%s&enterpriseId=%s", openId, tenantId));
        Request request = new Request.Builder()
                .url("https://id.inspuronline.com/console/api/v1/esg-saas/account/default-enterprise")
                .method("PUT", body)
                .addHeader("X-Api-Key", "c8ada5ec-252e-46f2-b71d-d888acf1a65c")
                .addHeader("Content-Type", "application/x-www-form-urlencoded")
                .build();
        this.httpClient.newCall(request).execute();
    }
}
